﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace MedievalGameProject
{
    public class BoundingShapes
    {
        //vert and effect for debug purposes
        private VertexBuffer vertBuff;
        private IndexBuffer indexBuff;
        private BasicEffect effect;
        private GraphicsDevice graphics;

        public BoundingBox outerBoundingBox;
        private List<BoundingBox> innerBoundingBoxes;
        private List<BoundingSphere> innerBoundingSpheres;
        public Vector3 pos;
        public Vector3 innerShapePos;

        public BoundingShapes(Vector2 pos, BoundingBox outerBoundingBox) : 
            this(new Vector3(pos.X, pos.Y, 0), outerBoundingBox)
        {
        }

        public BoundingShapes(Vector3 pos, BoundingBox outerBoundingBox)
        {
            this.pos = pos;
            this.innerShapePos = pos;
            outerBoundingBox.Min += pos;
            outerBoundingBox.Max += pos;
            this.outerBoundingBox = outerBoundingBox;
            innerBoundingBoxes = new List<BoundingBox>();
            innerBoundingSpheres = new List<BoundingSphere>();
        }

        public BoundingShapes(Vector3 pos, BoundingBox outerBoundingBox, GraphicsDevice graphics) : 
            this(pos,outerBoundingBox)
        {
            if (Game1.IsDebug())
            {
                this.graphics = graphics;
                VertexPositionColor[] verts = new VertexPositionColor[8];
                verts[0] = new VertexPositionColor(outerBoundingBox.Min, Color.Red);
                verts[1] = new VertexPositionColor(new Vector3(outerBoundingBox.Min.X, outerBoundingBox.Min.Y, outerBoundingBox.Max.Z), Color.Red);
                verts[2] = new VertexPositionColor(new Vector3(outerBoundingBox.Max.X, outerBoundingBox.Min.Y, outerBoundingBox.Min.Z), Color.Red);
                verts[3] = new VertexPositionColor(new Vector3(outerBoundingBox.Max.X, outerBoundingBox.Min.Y, outerBoundingBox.Max.Z), Color.Red);
                verts[4] = new VertexPositionColor(outerBoundingBox.Max, Color.Red);
                verts[5] = new VertexPositionColor(new Vector3(outerBoundingBox.Min.X, outerBoundingBox.Max.Y, outerBoundingBox.Min.Z), Color.Red);
                verts[6] = new VertexPositionColor(new Vector3(outerBoundingBox.Min.X, outerBoundingBox.Max.Y, outerBoundingBox.Max.Z), Color.Red);
                verts[7] = new VertexPositionColor(new Vector3(outerBoundingBox.Max.X, outerBoundingBox.Max.Y, outerBoundingBox.Min.Z), Color.Red);

                vertBuff = new VertexBuffer(graphics, typeof(VertexPositionColor), 8, BufferUsage.WriteOnly);
                vertBuff.SetData<VertexPositionColor>(verts);

                short[] indices = { 0, 1, 2, 1, 2, 3, //bottom face
                                4, 5, 7, 4, 5, 6, //top face
                                4, 3, 6, 3, 6, 0, //front face
                                5, 2, 0, 5, 2, 7, //back face
                                5, 1, 0, 5, 1, 6, //left face
                                4, 2, 3, 4, 2, 7  //right face
                              };

                indexBuff = new IndexBuffer(graphics, IndexElementSize.SixteenBits, 36, BufferUsage.WriteOnly);
                indexBuff.SetData<short>(indices);

                effect = new BasicEffect(graphics);
            }
        }

        public void AddInnerBoundingBox(BoundingBox box)
        {
            box.Min += innerShapePos;
            box.Max += innerShapePos;
            innerBoundingBoxes.Add(box);
        }

        public void AddInnerBoundingSphere(BoundingSphere sphere)
        {
            sphere.Center += innerShapePos;
            innerBoundingSpheres.Add(sphere);
        }

        public bool CheckCollision(BoundingShapes other)
        {
            if (other == null)
                return false;

            //Check if outer boxes intersect, if they do look at inner
            if(outerBoundingBox.Intersects(other.outerBoundingBox)) 
            {
                UpdateInnerPositions();
                other.UpdateInnerPositions();
                if (innerBoundingBoxes.Count == 0 && innerBoundingSpheres.Count == 0
                    && other.innerBoundingBoxes.Count == 0 && other.innerBoundingSpheres.Count == 0) 
                {
                    //If no inner shapes return true
                    return true;
                }
                else if (other.innerBoundingBoxes.Count == 0 && other.innerBoundingSpheres.Count == 0)
                {
                    //Only this one has innerBoxes
                    //Check outer bounding box against other's inner boxes
                    foreach (BoundingBox box in innerBoundingBoxes)
                    {
                        if (box.Intersects(other.outerBoundingBox))
                            return true;
                    }

                    //Check outer bounding box against other's inner spheres
                    foreach (BoundingSphere sphere in innerBoundingSpheres)
                    {
                        if (sphere.Intersects(other.outerBoundingBox))
                            return true;
                    }
                }
                else if (innerBoundingBoxes.Count == 0 && innerBoundingSpheres.Count == 0)
                {
                    //Only other one has innerBoxes
                    //Check outer bounding box against other's inner boxes
                    foreach (BoundingBox otherBox in other.innerBoundingBoxes)
                    {
                        if (outerBoundingBox.Intersects(otherBox))
                            return true;
                    }

                    //Check outer bounding box against other's inner spheres
                    foreach (BoundingSphere otherSphere in other.innerBoundingSpheres)
                    {
                        if (outerBoundingBox.Intersects(otherSphere))
                            return true;
                    }
                }
                else
                {
                    //Both have inner bounding boxes
                    //Check inner bounding boxes
                    foreach (BoundingBox box in innerBoundingBoxes)
                    {
                        //Check inner bounding boxes against other's inner boxes
                        foreach (BoundingBox otherBox in other.innerBoundingBoxes)
                        {
                            if (box.Intersects(otherBox))
                                return true;
                        }

                        //Check inner bounding boxes against other's inner spheres
                        foreach (BoundingSphere otherSphere in other.innerBoundingSpheres)
                        {
                            if (box.Intersects(otherSphere))
                                return true;
                        }
                    }

                    //Check inner spheres
                    foreach (BoundingSphere sphere in innerBoundingSpheres)
                    {
                        //Check inner spheres against other's inner boxes
                        foreach (BoundingBox otherBox in other.innerBoundingBoxes)
                        {
                            if (sphere.Intersects(otherBox))
                                return true;
                        }

                        //Check inner spheres against other's inner spheres
                        foreach (BoundingSphere otherSphere in other.innerBoundingSpheres)
                        {
                            if (sphere.Intersects(otherSphere))
                                return true;
                        }
                    }
                }
            }

            return false;
        }

        public bool PointInShapes(Vector3 point)
        {
            if (withinBox(outerBoundingBox, point))
            {
                return true;
            }

            return false;
        }

        public bool withinBox(BoundingBox box, Vector3 point)
        {
            return box.Min.X <= point.X && box.Min.Y <= point.Y && box.Min.Z <= point.Z
                && box.Max.X >= point.X && box.Max.Y >= point.Y && box.Max.Z >= point.Z;
        }

        public void UpdatePosition(Vector3 nextPos)
        {
            Vector3 velocity = nextPos - pos;
            this.pos.X += velocity.X;
            this.pos.Y += velocity.Y;
            this.pos.Z += velocity.Z;
            outerBoundingBox.Min += new Vector3(velocity.X, velocity.Y, velocity.Z);
            outerBoundingBox.Max += new Vector3(velocity.X, velocity.Y, velocity.Z);
        }

        public void UpdateInnerPositions()
        {
            Vector3 change = pos - innerShapePos;
            innerShapePos = pos;
            List<BoundingBox> boxes = new List<BoundingBox>();
            for (int i = 0; i < innerBoundingBoxes.Count; i++)
            {
                boxes.Add(new BoundingBox(innerBoundingBoxes[i].Min + change, innerBoundingBoxes[i].Max + change));
            }
            innerBoundingBoxes = boxes;

            List<BoundingSphere> spheres = new List<BoundingSphere>();
            for (int i = 0; i < innerBoundingSpheres.Count; i++)
            {
                spheres.Add(new BoundingSphere(innerBoundingSpheres[i].Center + change, innerBoundingSpheres[i].Radius));
            }
            innerBoundingSpheres = spheres;
        }

        public void Draw(GameTime gameTime)
        {
            if (Game1.IsDebug() && vertBuff != null)
            {
                graphics.SetVertexBuffer(vertBuff);
                graphics.Indices = indexBuff;
                effect.World = Matrix.CreateTranslation(pos);
                effect.View = Game1.camera.view;
                effect.Projection = Game1.camera.projection;
                effect.VertexColorEnabled = true;
                graphics.RasterizerState = RasterizerState.CullNone;

                foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                {
                    pass.Apply();
                    graphics.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, 12);
                }

                graphics.RasterizerState = RasterizerState.CullCounterClockwise;
            }
        }
    }
}
